//游戏区域的控制代码

//需要加入文件扩展名.js
import BlockUtil from "./BlockUtil.js"

//将游戏区域的行列数定义成常量
export const ROWS = 20,COLS = 10

//定义一个类Playfield,把游戏区域相关的逻辑进行封装
export default class Playfield{
    //position = {row: 0,col: 0}

    //构成游戏区域的所有小方块的信息，为二维数组
    //暂时置空，后面在构造函数里填充数据
    blocks = null

    constructor(type,rotate = 0,position){
        //创建20*10的空方块区域
       this.blocks = BlockUtil.createBlock(ROWS,COLS)
    }

    //碰撞检测实际上是检查移动或旋转后组成俄罗斯方块的每一个非空方块是否超出游戏区域的边界
    //或者和游戏区域已有的方块重叠
    isCollided(tetris,offsetRow,offsetCol,rotate){
        //复制一个俄罗斯方块的区块数据进行操作，而不是直接操作当前俄罗斯方块
        //因为碰撞检测应该是在移动或旋转操作之前完成的
        const tmpBlocks = BlockUtil.deepCopy(tetris.blocks)
        //取出俄罗斯方块当前所处的位置，重名为tr和tc
        let{ row: tr,col: tc} = tetris.position

        //把他们的值更新为俄罗斯方块移动后的值
        tr += offsetRow
        tc += offsetCol
        //旋转刚才复制的俄罗斯方块副本
        BlockUtil.rotate(tmpBlocks,rotate)

        //使用两层for循环依次遍历副本中的每一个小方块
        for(let r = 0;r < tmpBlocks.length; r++){
            //pr：是副本中小方块具体所在的行
            const pr = tr + r
            //如果小于0，说明还在游戏区域的上方，无需处理
            if(pr < 0) continue

            for(let c = 0;c < tmpBlocks[r].length; c++){
                //pc是副本中小方块具体所在的列
                const pc = tc + c

                //首先判定当前小方块是不是空方块
                //同时如果位置超出游戏区域边界
                //或者游戏区域中对应位置有“非空方块”
                //则返回true表示有碰撞
                if(tmpBlocks[r][c] 
                    && (pr >= ROWS || pc >= COLS || pc < 0 || this.blocks[pr][pc])){
                    return true;
                }
            }
        }
        //否则返回false，表示无碰撞
        return false;

    }

    //合并，固化
    merge(tetris){
        const { blocks: tBlocks, position } = tetris
        const { row: offsetRow, col: offsetCol } = position
        BlockUtil.merge(this.blocks, tBlocks, offsetRow, offsetCol)
    }

    //负责计算可以被消除的行，返回一个包含行行号的数组
    //from和to分别代表进行计算的起、止行
    //只有必要对当前固化的俄罗斯方块所占的那几行进行判定
    getClearableLines(from,to){
        //用来存储可以被消除的行的序号
        const lineToClear = []
        //在起止范围内逐行判定是否填满
        //循环上界取参数to和游戏区域总行数的较小值，避免循环越界
        for(let r = from; r < Math.min(to,ROWS); r++){
            //使用数组every（）方法判断遍历到的行中是否每一个小方块都不是空方块，也就是可以被消除的行
            if(this.blocks[r].every(c => !!c)){
                lineToClear.push(r)
            }
        }
        //判定完成后，返回这个数组
        return lineToClear

    }

    //执行消除操作的代码
    clearLines(lines){
        //循环遍历可以被消除的行，把它从blocks数组中移出
        //lines是要被消除的行的序号组成的数组，r是行序号
        //由 getClearableLines计算得出的值
        //如果删除了一行，相应的行号-1
        //执行删除操作，减去偏移量i
        lines.forEach((r,i) => this.blocks.splice(r-i,1))
        //createBlock创建相应行数的空方块区域插入到游戏区域的最上方
        //使用数组展开语法逐行插入
        this.blocks.unshift(...BlockUtil.createBlock(lines.length,COLS))
    }

    //清除游戏区域所有行的代码
    clearAll(){
        //只需要给所有行填充0
        this.blocks.forEach(r => r.fill(0))
    }

}